<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6</title>
    <link rel="stylesheet" href="../css/common.css">
    <link rel="stylesheet" href="../css/html.css">
</head>
<body>
<div class="container">
    <div class="catalogue">
		<div><a href="#es6">Es6</a></div>
		<div><a href="#sass">Sass</a></div>
		<div><a href="#vue">Vue.js</a></div>
		<div><a href="#react">React.js简介</a></div>
		<div><a href="#html-css">HTML CSS 面试题</a></div>
		<div><a href="#js">JS变量与原型链常见面试题分析</a></div>
		<div><a href="#js_core">JS作用域和闭包核心面试题分析</a></div>
		<div><a href="#js_thread">JS异步单线程与内置对象面试</a></div>
		<div><a href="#js-dom-bom">JS DOM、BOM事件常见面试题分析</a></div>

    </div>
    <div class="substance">
        <div class="one">
            <h1 id="es6">Es6
            </h1>
			<pre>
let和var的主要区别:
    (1)let声明的变量只在当前(块级)作用域内有效
    (2)let声明的变量不能被重复声明
    (3)不存在变量提升

ES6之前的作用域: 全局作用域 + 函数作用域 + eval作用域

块级作用域: 通俗的讲,就是一对花括号中的区域 { … }  + 块级作用域可以嵌套

const:常量(不可改变的量)
    (1)常量必须在声明的时候赋值
    (2)与let类似的特性: 不能重复声明  + 不存在变量提升  + 只在当前(块级)作用域内有效
    (3)常量为引用类型的时候,不能保证不可变
    (4)一旦声明常量 就不能再改变（引用类型需要冻结）

ES6变量的解构赋值: 解构赋值语法是一个 JavaScript 表达式,这使得可以将值从数组或属性从对象提取到不同的变量中。
        对象的解构赋值:
            (1)对象的解构赋值与数组的解构赋值相似
            (2)等号左右两边都为对象结构 - const { a, b } = {a: 1, b: 2}
            (3)左边的{}中为需要赋值的变量
            (4)右边为需要解构的对象
            (5)对象的解构赋值的主要用途: 提取对象属性 +  使用对象传入乱序的函数参数 + 获取多个函数返回值

        </pre>
			<h2 class="h2">const扩展总结:</h2>
			<ol>
				<li>
					一、数据属性描述符汇总如下：
					<table border="1" style="background-color: #00b3ea">
						<caption>数据属性描述器构成</caption>
						<tr>
							<td rowspan="5">数据/性质</td>
							<td>属性</td>
							<td>值类型</td>
							<td>默认值</td>
							<td>含义</td>
						</tr>
						<tr>
							<td>value</td>
							<td>任意类型</td>
							<td>undefined</td>
							<td>基本的名/值</td>
						</tr>
						<tr>
							<td>writable</td>
							<td>Boolean</td>
							<td>false </td>
							<td>属性可否重写，默认是只读</td>
						</tr>
						<tr>
							<td>enumerable </td>
							<td>Boolean</td>
							<td>false </td>
							<td>属性可否枚举，默认不能被 for in 枚举（循环遍历）</td>
						</tr>
						<tr>
							<td>configurable</td>
							<td>Boolean</td>
							<td>false </td>
							<td>属性可否被重新配置，默认 writable 和enumerable 可修改,且用 delete 删除</td>
						</tr>
					</table>
				</li>
				<li>
					二、Object.defineProperty( )函数和 Object.hasOwnProperty( )函数
					<pre>
1、Object.defineProperty( )函数
    （1）概念：将属性添加到对象上，或修改现有属性的特性
    （2）语法： Object.defineProperty(object, propertyName, descriptor)
    （3）参数：
            Object ：必需。被添加或修改属性的对象。
            propertyName：必需。一个包含属性名称的字符串
            descriptor：必需。属性描述符。它可以针对数据属性或访问器属性。
    （4）返回值：已修改的对象。
    （5）用法： 对象不存在此属性时,添加;否则更新此属性植.
    （6）举例：
        设置一个新属性,代码如下:
                        var obj = {name:'John'};
                        Object.defineProperty(obj,"newDataProperty",{value:101});//设置一个新属性
                        console.log(obj); // {name:"John",newDataProperty:101}  结果,可以设置新属性
        设置新属性并修改新属性的值,代码如下：
                        var obj = {name:'John'};
                        //设置一个新属性并设置可修改
                        Object.defineProperty(obj,"newDataProperty",{value:101,writable:true});
                        obj.newDataProperty = 102; //修改新属性的值
2、Object.hasOwnProperty( )函数
    （1）概念： 检测是否是原型链中的属性，如果是则返回 false，不是则返回 true（也就是自己自定义的属性）。
    （2）语法：object.hasOwnProperty(propertyName)
    （3）参数： propertyName：String 类型，指定的属性名称。
    （4）例子：
            是否可以检测出自定义的属性和方法,代码如下:
                    //定义一个对象并设置属性
                    const Person = {
                            name:"xm",
                            say:function(){ console.log("haha"); }
                    }
                    console.log("name: " + Person.hasOwnProperty("name") );//true
                    console.log("say: " + Person.hasOwnProperty("say") );//true

            是否可以检测出原型链中定义的属性和方法,代码如下:
                    //定义一个对象并设置属性
                    const Person = {
                            name:"xm",
                            say:function(){ console.log("haha"); }
                    }
                    Person.__proto__.age = 25;  //在原型链中添加一个属性
                    Person.prototype = { run: function(){ console.log('run'); } } //在原型链中添加一个方法
                    console.log("name: " + Person.hasOwnProperty("name") );//false
                    console.log("say: " + Person.hasOwnProperty("say") );  //false
                    结果：只能检测出自定义属性和方法，不能检测出原型链中的属性和方法
                </pre>
				</li>
				<li>
					三、对象的密封，冻结
					<pre>
1、Object.seal( ) 密封对象
    （1）概念：让一个对象密封，并返回被密封后的对象。密封对象是指那些不能添加新的属性，不能删除已有的属性，以及不能修改已有属性的可枚举性、可配置性、可写性，
        但是可以修改已有属性对象的值。
    （2）例子：
                添加一个新的属性，修改已有属性的值，删除已有的属性;代码如下:
                    const obj = { name:'John',sex:'Lady'}
                    obj.age = 30;
                    obj.name ="Rose";
                    delete obj.sex;//删除元素
                    console.log(obj); // {name:"Rose",age:30}
                    结果：可以添加一个新的属性，可以修改已有的属性，可以删除已有的属性

                密封对象并添加一个新的属性;代码如下:
                    const obj = {name:'John'}
                    Object.seal(obj);//密封
                    obj.age = 30;
                    console.log("obj.age:"+ obj.age);// obj.age:undefined
                    结果：密封对象之后就不能添加一个新的属性了。

                密封对象并修改已有属性的值;代码如下:
                    var obj = {name:'John'};
                    Object.seal(obj);//密封
                    obj.name = 'Leo';
                    console.log(obj.name);//Leo  结果：密封对象之后可以修改已有属性的值。
                    delete obj.name;
                    console.log(obj);//{name:'Leo'}   结果：密封对象之后不能删除已有的属性。

                密封对象并修改已有属性的可枚举性、可配置性、可写性。代码如下:
                    var obj ={name:'John'}
                    Object.seal(obj);//密封
                    //修改已有配置属性
                    Object.defineProperty(obj,'name',
                        {configurable:true,writable:true,enumerable:true}
                    )
                    结果：浏览器提示报错，密封对象之后不能修改已有属性的可枚举性、可配置性、可写性。

2、Object.freeze( )冻结对象
    （1） 概念： 这个方法比 Object.seal( )更厉害，
               冻结对象是指那些不能添加,不能修改,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性的对象。也就是说，这个对象永远是不可改变的。
    （2） 例子：只测试与 Object.seal( )不同的地方，也就是不能修改已有属性的值
            冻结对象并修改已有属性的值;代码如下:
                    var obj = { name:'John' }
                    Object.freeze(obj);//冻结
                    obj.name = 'Back';
                    console.log('obj.name:'+ obj.name); // obj.name:John
                    结果：冻结对象之后不能修改已有属性的值

                </pre>
				</li>
			</ol>
			<h2 class="h2">Generator(生成器)</h2>
			<pre>
1、 简介：可以把它理解成一个函数的内部状态的遍历器，每调用一次，函数的内部状态发生一次改变。
2、 写法：一是，function 关键字与函数名之间有一个星号；
        二是，函数体内部使用 yield 表达式，定义不同的内部状态
        三，ES6 没有规定，function 关键字与函数名之间的星号，写在哪个位置;
                    function * foo(){...}
                    function *foo(){...}
                    function* foo(){...}
                    function*foo(){...}
3、 next 方法,使得指针移向下一个状态。每次调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式（或 return 语句）为止;
    （1） yield 表达式：只有调用 next 方法才会遍历下一个内部状态，所以其实提供了一种可以暂停执行的函数。yield 表达式就是暂停标志。
            （再次调用 next 方法时，再继续往下执行，直到遇到下一个 yield 表达式。）
4、 例：如下，代码定义了一个 Generator 函数 f，它内部有两个 yield 表达式（hello 和world），即该函数有三个状态：hello，world 和 return 语句（结束执行）。
    （1） 必须调用遍历器对象的 next 方法，使得指针移向下一个状态。也就是说，每次调用 next 方法，内部指针就从函数头部或上一次停下来的地方开始执行，
            直到遇到下一个 yield 表达式（或 return 语句）为止。
                function* f(){
                    yield 'hello';
                    yield 'world';
                    return 'ending';
                }
                let hw = f();
                console.log(hw.next());// {value:'hello',done:false}
                console.log(hw.next());// {value:'world',done:false}
                console.log(hw.next());// {value:'ending',done:true}
                console.log(hw.next());// {value:undefined,done:true}
    （2） 代码一共调用了四次 next 方法。
        第一次调用，Generator 函数开始执行，直到遇到第一个 yield 表达式为止。
            next 方法返回一个对象，它的 value 属性就是当前 yield 表达式的值 hello，done 属性的值 false，表示遍历还没有结束。
        第二次调用，Generator 函数从上次 yield 表达式停下的地方，一直执行到下一个 yield 表达式。
            next 方法返回的对象的 value 属性就是当前 yield 表达式的值 world，done 属性的值 false，表示遍历还没有结束。
        第三次调用，Generator 函数从上次 yield 表达式停下的地方，一直执行到return 语句（如果没有 return 语句，就执行到函数结束）。
            next 方法返回的对象的 value 属性，就是紧跟在 return 语句后面的表达式的值
            （如果没有 return语句，则 value 属性的值为 undefined），done 属性的值 true，表示遍历已经结束。
        第四次调用，此时 Generator 函数已经运行完毕，next 方法返回对象的 value属性为 undefined，done 属性为 true。以后再调用 next 方法，返回的都是这个值。
    注：调用 Generator 函数,返回一个遍历器对象，代表 Generator 函数的内部指针。
            以后，每次调用遍历器对象的 next 方法，就会返回一个有着 value 和 done 两个属性的对象。
            value 属性表示当前的内部状态的值，是 yield 表达式后面那个表达式的值；done 属性是一个布尔值，表示是否遍历结束。
        </pre>
			<h2 class="h2">新方法的总结</h2>
			<pre>
一、Array.from(x,y) 第二个参数可选
    1、 作用：用于将类数组对象转化为数组；例：如下
                    let arrayLike = {
                        '0':'a', '1':'b', '2':'c',
                        length: 3
                    };
                    let arr2 = Array.from( arrayLike );
                    console.log(arr2); //["a","b","c"]
    2、 Array.from 还可以接受第二个参数，用来对每个元素进行处理，将处理后的值放入返回的数组；例：
                    let arrayLike = {
                        '0':'1', '1':'2', '2':'3',
                        length: 3
                    };
                    let arr2 = Array.from( arrayLike, x=>x*x );
                    console.log(arr2); //[1,4,9]
    3、 Array.from()还可以将字符串转为数组，然后返回字符串的长度

二、Array.of()
    1、 Array.of 方法用于将一组值，转换为数组。（总是返回参数值组成的数组。如果没有参数，就返回一个空数组）；例：
三、fill()
    1、 fill 方法使用给定值，来填充一个数组。fill 方法还可以接受第二个和第三个参数，用于指定填充的起始位置和结束位置；例：
                    console.log(['a','b','c','d'].fill(3,1,3)) // ["a",3,3,"d"]
四、includes()
    1、 该方法返回一个布尔值，表示某个数组是否包含给定的值。该方法的第二个参数表示搜索的起始位置，默认为 0。
        如果第二个参数为负数，则表示倒数的位置，如果这时它大于数组长度（比如第二个参数为-4，但数组长度为 3），则会重置为从 0开始。）
五、entries()，keys()和 values()
    1、 entries()，keys()和 values()——用于遍历数组。它们都返回一个遍历器对象，可以用 for...of 循环进行遍历，
            区别是 keys()是对键名的遍历、values()是对键值的遍历，entries()是对键值对的遍历

六、find()和 findIndex()
    1、 find()用于找出第一个符合条件的数组成员。它的参数是一个回调函数，所有数组成员依次执行该回调函数，直到找出第一个返回值为 true 的成员，然后返回该成员。
            如果没有符合条件的成员，则返回 undefined。
            let arr =[1,4,-5,10,-3]
            console.log(arr.find( (n)=> n<0) ); //-5
    2、 findIndex()返回第一个符合条件的数组成员的位置，如果所有成员都不符合条件，则返回-1。
            let arr =[1,4,-5,10,-3]
            console.log(arr.findIndex( (n)=> n<0) ); // 2
    3、 这两个方法都可以接受第二个参数，用来绑定回调函数的 this 对象。例：
                    let arr = [10,12,26,15]
                    function f(v) { return v > this.age; }
                    let person = {name:'Leo',age:20};
                    console.log(arr.find(f,person)); //26
                    console.log(arr.findIndex(f,person)); //2
        </pre>
			<h2 class="h2">字符串扩展</h2>
			<pre>
部分新的方法:
            (1)includes() 方法用于检测一个字符串是否包含在另一个字符串中，返回布尔值。
            (2)startsWith() 方法用于检测字符串是否以指定的子字符串开头，返回布尔值。
            (3)endsWith() 方法用于检测字符串是否以指定的子字符串结尾，返回布尔值。
            (4)repeat() 方法用于重复一个字符串指定次数。
            (5)normalize() 方法用于规范化字符串，以便统一Unicode字符的不同表示形式。
                    const str ='\u00c5';
                    console.log( str.normalize('NFC') );
            (6)模板字符串使用反引号 (`) 而不是单引号或双引号，支持多行文本和插值表达式。
                let name = 'leo';
                let age = 30;
                console.log(`${name} is ${age} years old.`);
            (7)padStart() 和 padEnd()
                padStart() 和 padEnd() 分别用于在字符串头部和尾部添加指定字符直至达到给定长度。
            (8)新的Unicode表示法和遍历方式
                Unicode和UTF-16是什么和他们的关系 + 用for-of遍历字符
            (9)其他
                    正则扩展： 构造函数的变化 + 新的修饰符(uy修饰符) + 新的属性和与结构赋值结合使用(具名组)
                    数值扩展： 新的进制表示法 + 新的方法与安全数
                    函数扩展：默认参数 + 与扩展运算符的结合 + 箭头函数 (=>)
                    对象扩展： 简介表示法与属性名表达式 + 部分新方法与扩展运算符
                    数组扩展： 和扩展运算符结合 + 新的方法
        </pre>
			<h2 class="h2">Promise</h2>
			<pre>
什么是Promise: Promise对象用于表示一个异步操作的最终状态（完成或失败）以及其返回的值。

同步？异步？
    同步任务会阻塞程序执行(alert、for、…….)
    异步任务不会阻塞程序执行(setTimeout、fs.readFile、…...)

Promise:
    使用Promise(then、catch、finally)
    Promise.all & Promise.race
    Promise.resolve & Promise.rejcet

Promise的三种状态: pending(进行中) + fulfilled(成功) + rejected(失败)
    状态的改变不可逆,一但决议就不能再修改


总结:
        • 改善了传统回调造成的代码难维护、控制反转等问题
        • promise是异步的
        • 如果all接受的是空数组 马上会被决议成功
        • 如果race接受的是空数组 那会永远挂起
        • 无限捕获错误问题

resolve和reject方法
        • 如果接受的是普通的值 就会立即决议为成功 并填充这个值
        • 如果接受的是一个promise实例 则返回这个promise实例
        • 如果接受的是个thenable对象 则会把他包装成promise对象 并立即执行该对象的then方法
        • reject会产生一个决议失败的promise 并直接传递值
            </pre>
			<h2 class="h2">es6 class</h2>
			<pre>
课程内容
        ◆ ES6中类的特性
        ◆ 类与对象
        ◆ 类的继承
        ◆ Babe
        ◆ 基于流程控制的形变类实现

课程收获:
        ◆ ES5中的类
        ◆ 什么是类和对象
        ◆ 其他面向对象相关内容(多态等)
        ◆ Babel的使用
        ◆ ES6中的类


OOP – 面向对象开发: 核心 -> 封装


类与对象 – 更高级的面向对象简介:
                三大基本特性: 多态 + 继承 + 封装

Babel 是一个 JavaScript 编译器:
                Browserslist:  https://github.com/browserslist/browserslist

课程总结
                ◆ 类与对象
                ◆ ES6中类的特性
                ◆ 类的继承
                ◆ Babel
                ◆ 基于流程控制的形变类实现

            </pre>

        </div>
		<div class="one">
			<h1 id="sass">
				Sass  (css扩展语言)
				|<a href="https://file.mukewang.com/class/assist/823/4558021/92izxal9fgt/Sasssublime%E7%BC%96%E8%BE%91%E5%99%A8%E6%8F%92%E4%BB%B6%E7%BC%96%E8%AF%91%E6%96%B9%E6%B3%95.pdf" target="_blank">windows下安装sass</a>
			</h1>
			<pre>
课程面向用户
    • Jser（有一定的基础，但是想进一步了解Sass与项目的结合）
    • Csser（经常和css打交道，但是不知道如何进阶和优化）

了解和掌握Sass的基本语法
了解和掌握Sass的核心用法
了解和掌握如何使用Sass开发一个实战页面

Sass整体介绍: 语言 + 框架 + 工具
CSS预处理工具: 前置文件 + 编译解析 + CSS + 语法 + 编译环境 + 工具库
Sass工作流程: .sass .scss + Sass工具 + .css  ==> HTML

Sass能帮助我们解决什么问题:
    嵌套规则: 嵌套规则通过花括号的方式解决复杂的css父子样式嵌套问题。
    变量规则: 通过变量将公共样式抽离，减少冗余css代码。
    条件逻辑: 像高级语言一样编写逻辑性的css代码

Sass环境安装和编译:
    (1)安装Sass:  安装Ruby + 使用Ruby安装scss
            // 1.删除原gem源
            gem  sources  --remove  https://rubygems.org/
            // 2.添加淘宝源
            gem  sources  -a  https:://ruby.taobao.org/
            // 3.打印是否替换成功
            gem  sources -l
            // 4.更换成功后打印如下
            *** CURRENT SOURCES ***
            https:://ruby.taobao.org/
    (2)编译Sass--命令行
            // 单文件转换命令
            sass  input.scss  output.css

            //单文件监听命令
            sass  --watch  input.scss:output.css

            //如果你有很多的sass文件的目录，你也可以告诉sass监听整个目录
            sass  --watch  app/sass:public/stylesheets
    (3) 编译Sass —-> sublime SassBuild
    (4) 编译Sass —-> koala
        </pre>
			<h2 class="h2">Sass基础知识</h2>
			<pre>
Sass变量和引用:
    变量格式
    变量类型
    变量作用域
    import规则等

Sass数据类型，变量运算，mixin:
            数字类型，颜色类型，字符串类型
            变量加减乘除运算
            mixin用法

Sass继承和嵌套:
            选择器嵌套，属性嵌套，伪类嵌套
            简单继承，多继承，链式继承等

Sass条件控制:
            @if,@for,@while,@each等条件控制语句

Sass函数:
            内置函数(字符串函数，数字函数等)
            自定义函数使用方法



        </pre>
		</div>
		<div class="one">
			<h1 id="vue">Vue.js
			| <a target="_blank" href="https://file.mukewang.com/class/assist/1448/4558021/83phl6q6g1h/vue.js%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.pdf">vue.js 开发环境搭建</a>
			</h1>
		</div>
		<div class="one">
			<h1 id="react">React.js简介</h1>
			<pre>
开发环境搭建:
    引入.js文件来使用React
    通过脚手架工具来编码
    Create-react-app

React的核心特性总结:
    声明式开发
    可以与其他框架并存
    组件化
    单向数据流
    函数式编程

Props, State 与 render 函数

React 中 ref 的使用

React 中的生命周期函数
            </pre>
			<img src="../image/react/react生命周期函数.png" alt="1">
			<h2 class="h2">生命周期函数的使用实例</h2>
			<h2 class="h2">Ant Design 组件库的使用</h2>
			<h2 class="h2">React 中的前端路由</h2>
			<pre>

Header 区块的制作

                列表页面的制作及路由配置
                使用动态路由获取不同列表内容
                详情页面的制作
                登陆功能的制作

            </pre>
		</div>
		<!--HTML CSS 面试题-->
		<div class="one">
			<h1 id="html-css">HTML CSS 面试题</h1>
			<pre>
(1)如何理解 HTML 语义化？
                ◆ 让人更容易读懂（增加代码可读性）
                ◆ 让搜索引擎更容易读懂 （SEO）
(2)默认情况下，哪些 HTML 标签是块级元素、哪些是内联元素？
                ◆ display: block/table; 有 div h1 h2 table ul ol p 等
                ◆ display: inline/inline-block; 有 span img input button 等



CSS 面试题:
    分析知识模块: ◆ 响应式 + ◆ 定位 + ◆ 布局 + ◆ 图文样式 + ◆ CSS3
CSS - 布局:
        ◆ 盒子模型的宽度如何计算？
                ◆ offsetWidth = ( 内容宽度 + 内边距 + 边框 )，无外边距
                ◆ box-sizing: border-box;

                题目: 如下代码,请问div1 的offsetWidth 是多少?  122px
                #div1{ width:100px; padding:10px; border: 1px solid #ccc; margin: 10px }

                补充：如果让 offsetWidth 等于 100px ，该如何做？
                #div1{ width:100px; padding:10px; border: 1px solid #ccc; margin: 10px;box-sizing: border-box }



        ◆ margin 纵向重叠的问题
                ◆ 相邻元素的 margin-top 和 margin-bottom 会发生重叠
                ◆ 空白内容的 &lt;p>&lt;/p> 也会重叠

                题目:如下代码, AAA 和 BBB 之间的距离是多少?  15px
                p{ font-size:16px; line-height: 1; margin: 10px 0 15px 0;}
                &lt;p>AAA&lt;/p>
                &lt;p>
                &lt;p>
                &lt;p>
                &lt;p>BBB&lt;/p>

        ◆ margin 负值的问题
                ◆ margin-top 和 margin-left 负值，元素向上、向左移动
                ◆ margin-right 负值，右侧元素左移，自身不受影响
                ◆ margin-bottom 负值，下方元素上移，自身不受影响
        ◆ BFC 理解和应用
                ◆ Block format context ，块级格式化上下文
                ◆ 一块独立渲染区域，内部元素的渲染不会影响边界以外的元素
                ◆ 形成 BFC 的常见条件
                        ◆ float 不是 none
                        ◆ display 是 flex inline-block 等
                        ◆ position 是 absolute 或 fixed
                        ◆ overflow 不是 visible
                ◆ BFC 的常见应用: 清除浮动
        ◆ float 布局的问题，以及 clearfix
                float 布局:
                        ◆ 如何实现圣杯布局和双飞翼布局
                        ◆ 手写 clearfix
                                /* 手写 clear fix */
                                .clearfix:after{ content: ' ';display: table; clear: both }
                                .clearfix{ *zoom:1; } /* 兼容 IE低版本 */
                圣杯布局和双飞翼布局的目的:
                    ◆ 三栏布局，中间一栏最先加载和渲染（内容最重要）
                    ◆ 两侧内容固定，中间内容随着宽度自适应
                    ◆ 一般用于 PC 网页
                圣杯布局和双飞翼布局的技术总结;
                    ◆ 使用 float 布局
                    ◆ 两侧使用 margin 负值，以便和中间内容横向重叠
                    ◆ 防止中间内容被两侧覆盖，一个用 padding 一个用 margin
        ◆ flex 画色子
                常用语法回顾: flex-direction  flex-wrap   justify-content     align-self  align-items
                /* flex 画三个点的色子 */
                .box{
                    display:flex; /*flex布局*/
                    justify-content: space-between; /*两端对齐*/
                }
                .item{
                    /*背景色/大小/边框等*/
                }
                .item:nth-child(2){
                    align-self: center; /*第二项居中对齐*/
                }
                .item:nth-child(3){
                    align-self: flex-end; /*第三项尾对齐*/
                }


CSS - 定位:
                ◆ absolute 和 relative 分别依据什么定位？
                    ◆ relative 依据自身定位
                    ◆ absolute 依据最近一层的定位元素定位
                ◆ 居中对齐有哪些实现方式？
                    水平居中:
                            ◆ inline 元素：text-align: center
                            ◆ block 元素：margin: auto
                            ◆ absolute 元素：left: 50% + margin-left 负值
                    垂直居中:
                            ◆ inline 元素：line-height 的值等于 height 值
                            ◆ absolute 元素：top: 50% + margin-top 负值
                            ◆ absolute 元素： transform(-50%, -50%)
                            ◆ absolute 元素：top, left, bottom, right = 0 + margin: auto

CSS - 图文样式:
                ◆ line-height 的继承问题
                        ◆ 写具体数值，如 30px ，则继承该值（比较好理解）
                        ◆ 写比例，如 2 / 1.5 ，则继承该比例（比较好理解）
                        ◆ 写百分比，如 200% ，则继承计算出来的值（考点）
                如下代码,p标签的行高是多少?
                body{ font-size: 20px; line-height: 200%; }
                p{ font-size: 16px }
                &lt;body>
                        &lt;p>AAA&lt;/p>
                &lt;/body>


CSS - 响应式:
                ◆ 响应式布局的常用方案
                        ◆ media-query，根据不同的屏幕宽度设置根元素 font-size
                        ◆ rem，基于根元素的相对单位

                ◆ rem 是什么？
                    rem 是一个长度单位;
                        ◆ px ，绝对长度单位，最常用
                        ◆ em ，相对长度单位，相对于父元素，不常用
                        ◆ rem ，相对长度单位，相对于根元素，常用于响应式布局

CSS - CSS3:
                ◆ 关于 CSS3 动画

            </pre>
		</div>
		<div class="one">
			<h1 class="h1" id="js">JS变量与原型链常见面试题分析</h1>
			<pre>
题目:
                • JS 中使用 typeof 能得到的哪些类型
                • 何时使用 === 何时使用 ==
                • JS 中有哪些内置函数
                • JS 变量按照存储方式区分为哪些类型，并描述其特点
                • 如何理解JSON

变量类型:
    • 值类型 vs 引用类型
    • typeof 运算符详解
                typeof undefined // undefined
                typeof 'abc'     // string
                typeof 123       // number
                typeof true       // boolean
                typeof {}       // object
                typeof []       // object
                typeof null       // object
                typeof console.log       // function


变量计算 - 强制类型转换
    • == 运算符:
                100 == '100' //true
                0 == '' // true
                null == undefined //true
    • 字符串拼接:
                let a = 100 + 10     // 110
                let a = 100 + '10';  //'10010'
    • if 语句
    • 逻辑运算:
                console.log( 10 && 0); // 0
                console.log( '' || 'abc' ); // 'abc'
                console.log( !window.abc); // true
                // 把变量转为 boolean
                let a = 100;
                console.log( !!a );

JS内置函数: Object + Array + Boolean + Number + String + Function + Date + RegExp + Error

JS按存储方式区分变量类型;

如何理解JSON? JSON只不过是一个 JS 对象而已.



            </pre>
			<h2 class="h2">原型和原型链</h2>
			<pre>
题目:
    • 如何准确判断一个变量是数组类型
    • 写一个原型链继承的例子
    • 描述 new 一个对象的过程


知识点
    • 构造函数
                function Foo(name,age){
                    this.name = name;
                    this.age = age;
                    this.class = 'class-1';
                    // return this; // 默认有这一行
                }
                let f = new Foo('Leo',20)
    • 构造函数 - 扩展
                • var a = {} 其实是 var a = new Object() 的语法糖
                • var a = [] 其实是 var a = new Array() 的语法糖
                • function Foo(){...} 其实是 var Foo = new Function(...)
                • 使用 instanceof 判断一个函数是否是一个变量的构造函数
    • 原型规则和示例
                • 所有的引用类型（数组、对象、函数），都具有对象特性，即可自由扩展属性（除了“null”意外）
                • 所有的引用类型（数组、对象、函数），都有一个__proto__属性，属性值是一个普通的对象
                • 所有的函数，都有一个 prototype 属性，属性值也是一个普通的对象
                • 所有的引用类型（数组、对象、函数），__proto__属性值指向它的构造函数的 ”prototype“ 属性值
                • 当试图得到一个对象的某个属性时，如果这个对象本身没有这个属性，那么会去它的 __proto__（即它的构造函数的prototype）中寻找。
    • 原型链
                <img alt=" " src="../image/react/原型链-1.png" width="80%" height="60%">
                <img alt=" " src="../image/react/原型链-2.png" width="80%" height="60%">
    • instanceof
                用于判断 引用类型 属于哪个 构造函数 的方法
                • f instanceof Foo 的判断逻辑是：
                • f 的 __proto__ 一层一层往上，能否对应到 Foo.prototype
                • 再试着判断 f instanceof Object
如何准确判断一个变量是数组类型:
                let arr = []
                arr instanceof Array //true
                typeof arr //object
写一个原型链继承的例子:
                //动物
                function Animal(){
                    this.eat = function(){ console.log('animal eat'); }
                }
                //狗
                function Dog(){ this.bark = function(){ console.log('dog bark') } }
                Dog.prototype = new Animal();
                //哈士奇
                let h = new Dog();

描述 new 一个对象的过程:
                • 创建一个新对象
                • this 指向这个新对象
                • 执行代码，即对 this 赋值
                • 返回 this

            </pre>
			<h2 class="h2">常见面试题:</h2>
			<pre>
# 答题
### JS中使用`typeof`能得到的哪些类型? 针对这个题目，可以通过以下程序进行验证
        ```javascript
        typeof undefined // undefined
        typeof 'abc' // string
        typeof 123 // number
        typeof true // boolean
        typeof {}  // object
        typeof [] // object
        typeof null // object
        typeof console.log // function
        ```
### 何时使用`===` 何时使用`==`
    首先你得明白两者的区别。`==`会先试图类型转换，然后再比较，而`===`不会类型转换，直接比较。如下例子：
        ```javascript
        1 == '1' // true
        1 === '1' // false
        0 == false // true
        0 === false // false
        null == undefined // true
        null === undefined // false
        ```
        根据 jQuery 源码中的写法，只推荐在一个地方用`==`，其他地方都必须用`===`。这个用`==`的地方就是：
        ```javascript
            if (obj.a == null) {  // 这里相当于 obj.a === null || obj.a === undefined ，简写形式
            }
        ```
    编程是需要绝对严谨的态度，我们只在这一个地方让它进行类型转换，来简化我们的写法，因为这个场景非常简单和固定。而其他场景下，我们都必须使用`===`，除非有特殊的业务需要。

### JS中有哪些内置函数 —— 数据封装类对象:`Object` `Array` `Boolean` `Number` `String` `Function` `Date` `RegExp` `Error`
    对于这种问题，回复时能把基本常用的回答上来就可以，没必要背书把所有的都写上。

### JS变量按照存储方式区分为哪些类型，并描述其特点
- 值类型 `undefined` `string` `number` `boolean`
- 引用类型 `object` `function`
最后补充一点，在 JS 中，所有的引用类型都可以自由设置属性
        ```javascript
        var obj = {}
        obj.a = 100

        var arr = []
        arr.a = 100

        function fn() {}
        fn.a = 100
        ```

### 如何理解JSON
这个问题，很容易被一些初学者误答。其实，JSON 是什么？从 JS 角度回答，太简单了，`console.log(JSON)`得到`JSON`只是一个对象，有`parse`和`stringify`两个方法，使用也非常简单
    ```javascript
    JSON.stringify({a:10, b:20})
    JOSN.parse('{"a":10,"b":20}')
    ```
我之所以误答，就是怕初学者把这个问题搞大，因为 json 也是一种数据格式，这一点和 xml 一样。
但是在 JS 的面试题中，如果问到这个问题，直接说明`parse`和`stringify`两个方法的用法即可，面试官如果有追问，你再去继续回答。


# 知识点
## 变量类型
JS变量最基本的分类就是**值类型**和**引用类型**，两者有何区别呢，可以通过例子看出来。

以下是值类型的一个例子:
    var a = 100
    var b = a
    a = 200
    console.log(b)

以下是引用类型的一个例子:
    var a = {age:20}
    var b = a
    b.age = 21
    console.log(a.age)

typeof可以知道一个值类型是什么类型，而对于引用类型，它就无能为力了。但是它可以将引用类型区分出`function`，为什么 ?
因为`function`相对于其他引用类型（如对象、数组）来说，具有非常特殊的意义，JS 中的函数非常重要，接下来的原型、作用域都会深入讲解函数。
JS 中的某些表现，就已经体现了函数的特殊意义，
例如：对象和数组，JS中没有内置的（不考虑 JS-WEB-API），而函数却内置了很多，例如 `Object` `Array` `Boolean` `Number` `String` `Function` `Date` `RegExp` `Error`。
这些函数 JS 本身就有，要是没有它们，就没法愉快的写 JS 代码了。因为他们是基础数据类型的构造函数（后面会讲解）
`typeof`可以区分类型有`number` `string` `boolean` `undefined`（值类型） `function` `object`（引用类型）
特例: typeof null // object 因为 null 也是引用类型。null 就相当于引用类型中的 undefined

那么针对第二个例子，如何将`a`的内容复制给`b`，并且保证`b`的修改不会影响到`a`呢？
那就需要**深度复制**，意思就是对`a`的属性进行递归遍历，再依次复制，这块我们会放在后面专门讲解。

## 变量计算
> 本节专门讲解值类型的计算，引用类型的计算放在后面作为“JS 算法”统一讲解。
JS 在值类型的运算过程中，特别需要注意和利用**强制类型转换**这一特性，有以下场景：
    字符串拼接
    ==
    逻辑运算（`if` `!` `||` `&&`）

字符串拼接最常见的错误如下，特别要注意。如何规避呢 ———— 对进行计算的变量通过`typeof`来判断类型 ———— 太麻烦？编码本身就是一个体力活！
var a = 100 + 10   // 110
var b = 100 + '10' // '10010'

接下来，`==`也会进行强制类型转换，如
```javascript
100 == '100'   // true
0 == ''  // true
null == undefined  // true
```

针对`100 == '100'`就是和拼接字符串一样的类型转换，而针对下面两个例子，就是一个逻辑运算上的强制类型转换（马上会讲解）。
所以，要求你写 JS 代码时，所有的地方都要使用`===`而不能使用`==`，但是阅读 jquery 源码后我发现一个特例，就是`obj.a == null`，使用很简洁。
最后，逻辑运算中的强制类型转换，先以`if`为例说明
        ```javascript
        var a = true
        if (a) {
            // ....
        }
        var b = 100
        if (b) {
            // ....
        }
        var c = ''
        if (c) {
            // ....
        }
        ```
所有经过`if`判断的变量，都会进行逻辑运算的强制类型转换，转换为`true`或者`false`。
```javascript
    console.log(10 && 0)  // 0
    console.log('' || 'abc')  // 'abc'
    console.log(!window.abc)  // true
    // 判断一个变量会被当做 true 还是 false
    var a = 100
    console.log(!!a)
```

日常开发中，以下变量会被转换为false: 0,NaN,'',null,undefined,false本身
除了以上几个，其他的都会被转换为`true`。

除了`if`之外，`!` `||` `&&`这三个运算符也会进行同样的转换，跟`if`是一个道理**。因此，如何快速判断一个变量将会被`if`转换为什么呢？————`!!a`
            </pre>
			<h2 class="h2">原型和原型链知识点</h2>
			<pre>
# 知识点
JS 是基于原型的语言，原型理解起来非常简单。任何长存不会被遗弃和提到的东西，都是最简单的东西。

## 构造函数
所有的 JS 入门教程都会有类似这样的例子
    ```javascript
    function Foo(name, age) {
        this.name = name
        this.age = age
        this.class = 'class-1'
        // return this  // 默认有这一行
    }
    var f = new Foo('zhangsan', 20)
    // var f1 = new Foo('lisi', 22)  // 创建多个对象
    ```
以上示例是通过`new Foo`创建出来一个`f`对象，对象有`name` `age` `class`三个属性，这样我们就称`Foo`是`f`的**构造函数**。
构造函数这个概念在高级语言中都存在，它就像一个模板一样，可以创建出若干个示例。

函数执行的时候，如果前面带有`new`，那么函数内部的`this`在执行时就完全不一样了，不带`new`的情况我们下一章节会讲到。
带`new`执行时，函数中的`this`就会变成一个空对象，让程序为其属性赋值，然后最终返回。`return this`是默认执行的，
如何验证？———— 你可以最后加一个`return {x:10}`试一下。返回之后，`f`就被赋值成了这个新对象，这样就创建完成了。

## 构造函数 - 扩展
- `var a = {}`其实是`var a = new Object()`的语法糖
- `var a = []`其实是`var a = new Array()`的语法糖
- `function Foo(){...}`其实是`var Foo = new Function(...)`的语法糖

大家看到以上几点，明白我要表达的意思了吗？

**如何判断一个函数是否是一个变量的构造函数呢 ———— 使用`instanceof`**，原理接下来就会讲到。
## 几个要点

以下要点，要全部明白并且记住！！！在此我会详细解释
- **所有的引用类型（数组、对象、函数），都具有对象特性，即可自由扩展属性（除了`null`意外）**
- **所有的引用类型（数组、对象、函数），都有一个`__proto__`属性，属性值是一个普通的对象**
- **所有的函数，都有一个`prototype`属性，属性值也是一个普通的对象**
- **所有的引用类型（数组、对象、函数），`__proto__`属性值指向它的构造函数的`prototype`属性值**

    ```javascript
    var obj = {}; obj.a = 100;
    var arr = []; arr.a = 100;
    function fn () {}
    fn.a = 100;

    console.log(obj.__proto__);
    console.log(arr.__proto__);
    console.log(fn.__proto__);

    console.log(fn.prototype)

    console.log(obj.__proto__ === Object.prototype)
    ```

## 原型

我们先将一开始的示例做一下改动，然后看一下执行的效果

```javascript
// 构造函数
function Foo(name, age) {
    this.name = name
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
// 创建示例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
// 测试
f.printName()
f.alertName()
```

执行`printName`时很好理解，但是执行`alertName`时发生了什么？这里再记住一个重点 **当试图得到一个对象的某个属性时，如果这个对象本身没有这个属性，那么会去它的`__proto__`（即它的构造函数的`prototype`）中寻找**

那么如何判断一个这个属性是不是对象本身的属性呢？使用`hasOwnProperty`，常用的地方是遍历一个对象的时候

```javascript
var item
for (item in f) {
    // 高级浏览器已经在 for in 中屏蔽了来自原型的属性，但是这里建议大家还是加上这个判断，保证程序的健壮性
    if (f.hasOwnProperty(item)) {
        console.log(item)
    }
}
```

## 原型链

还是接着上面的示例，执行`f.toString()`时，又发生了什么？因为`f`本身没有`toString()`，并且`f.__proto__`（即`Foo.prototype`）中也没有`toString`。这个问题还是得拿出刚才那句话————**当试图得到一个对象的某个属性时，如果这个对象本身没有这个属性，那么会去它的`__proto__`（即它的构造函数的`prototype`）中寻找**

如果在`f.__proto__`中没有找到`toString`，那么就继续去`f.__proto__.__proto__`中寻找，因为`f.__proto__`就是一个普通的对象而已嘛！

这样一直往上找，你会发现是一个链式的结构，所以叫做“原型链”。直到找到最上层都没有找到，那么就宣告失败，返回`undefined`。最上层是什么 ———— `Object.prototype.__proto__ === null`

## 原型链中的`this`

所有的从原型或者更高级的原型中得到、执行的方法，其中的`this`在执行时，就指向了当前这个触发事件执行的对象。因此`printName`和`alertName`中的`this`都是`f`。

## instanceof

开始介绍的`instanceof`这里再讲一下原理。如果要计算`f instanceof Foo`是不是正确，就要判断`f`的原型一层一层往上，能否对应到`Foo.prototype`。同理，如果要计算`f instanceof Object`是不是正确，就要判断`f`的原型一层一层往上，能否对应到`Object.prototype`

## 扩展参考

讲解原型和原型链，绝对要参考我之前写过的文章[《深入理解JS原型和闭包系列博客》](http://www.cnblogs.com/wangfupeng1988/p/3977924.html)


            </pre>
			<h2 class="h2">答题</h2>
			<pre>
# 答题
### 如何**准确**判断一个变量是数组类型
只有`instanceof`才能判断一个对象是否是真正的数组，`instanceof`的具体原理后面会讲到。
```javascript
var arr = []
arr instanceof Array // true
typeof arr // object，typeof 是无法判断是否是数组的
```

扩展：实际应用中，和数组同样重要、起同样作用并且更加灵活的数据结构还是“伪数组”或者“类数据”（jquery 就用到了）。因此，在实际应用中，只需要判断`length`属性是否是数字即可。
```javascript
var arr = []
var likeArr = {
    0: 'aaa',
    1: 'bbb',
    2: 'ccc',
    length: 3
}

typeof arr.length === 'number' // true
typeof likeArr.length === 'number' // true
```

PS：**为何需要要扩展补充？** 在面试过程中，面试官很希望能从你那里得到“惊喜”，即面的他提问的问题，你正确回答之后，还能有所补充，这样你会加分不少。
在日常工作中也一样，如果你能在完成工作之后再去考虑如何更有质量、更有效率的完成工作，或者通过本次工作的总结出一种方式，能更好的完成接下来的工作，那你的 leader 绝对高看你一眼。这其实就是我们所说的**积极、主动和工作热情**，光嘴说没用，你得实干。

### 写一个原型链继承的例子
接下来继续回答这个问题。你看其他人的培训或者看书，这个例子一般都会给你弄一些小猫小狗小动物来演示，例如

```javascript
// 动物
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}
// 狗
function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}
Dog.prototype = new Animal()
// 哈士奇
var hashiqi = new Dog()

// 其实，书中是为了演示了而演示，真正的实际 JS 开发中，根本不推荐这种两层甚至三层的继承。因为工作中项目本身业务就非常复杂，代码设计上就尽量求简，越简单的东西才越容易扩展和改变。
```
这种方式也可以准确回答问题，但是这并不是面试官真正想要的，面试官想要的是一个真正基于实战的 demo 而不是只看到了书本确没有应用（我可以帮助你解读面试官的诉求，这是面试中非常关键的一点）
接下来，我将根据自己的开源项目 [wangEditor](https://github.com/wangfupeng1988/wangEditor)
中的[一段源码](https://github.com/wangfupeng1988/wangEditor/blob/v3/src/js/util/dom-core.js)进行简化，通过实际的业务场景来使用原型和继承

```javascript
// 构造函数
function DomElement(selector) {
    var result = document.querySelectorAll(selector)
    var length = result.length
    var i
    for (i = 0; i < length; i++) {
        this[i] = selectorResult[i]
    }
    this.length = length
}
// 修改原型
DomElement.prototype = {
    constructor: DomElement,
    get: function (index) {
        return this[index]
    },
    forEach: function (fn) {
        var i
        for (i = 0; i < this.length; i++) {
            const elem = this[i]
            const result = fn.call(elem, elem, i)
            if (result === false) {
                break
            }
        }
        return this
    },
    on: function (type, fn) {
        return this.forEach(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
}

// 使用
var $div = new DomElement('div')
$div.on('click', function() {
    console.log('click')
})
```


### 描述 new 一个对象的过程

```javascript
function Foo(name) {
    this.name = name
    this.type = 'foo'
}
var foo = new Foo('beijing')
```

- 创建一个新对象
- `this`指向这个新对象
- 执行代码，即对`this`赋值
- 返回`this`

### zepto（或其他框架） 源码中如何使用原型链

解读优秀开源框架的源码或者设计，是成为程序大牛的毕竟之路。当然，解读源码是一件非常枯燥、成本非常高的事儿。
因此我强烈推荐，不要去一意孤行闭门造车的解读枯燥的源码，而是通过在网上找到一些优秀的材料，来解读一个框架的设计。

就像一本厚厚的书，如何快速掌握书的内容？———— 知道个大致的故事结构，然后去看看别人（最好是名人）的一些点评，
这样虽然花费时间很少，但是你也能比那些闷着头把书看完的人，了解到的东西更多。而且这种方式你可以高效率的阅读很多书，增加你的见识。
参考我在慕课网的视频教程[zepto设计和源码](http://www.imooc.com/learn/745)，免费的。
            </pre>

		</div>
		<div class="one">
			<h1 id="js_core">JS作用域和闭包核心面试题分析</h1>
			<pre>
### 说一下对变量提升的理解
函数执行时会先创建当前的上下文环境，其中这两点会产生“变量提升”的效果
- 变量定义
- 函数声明

### 说明 this 几种不同的使用场景
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
- call apply bind

### 创建 10 个`&lt;a>`标签，点击的时候弹出来对应的序号

错误的写法
```javascript
var i, a
for (i = 0; i < 10; i++) {
    a = document.createElement('a')
    a.innerHTML = i + '&lt;br>'
    a.addEventListener('click', function (e) {
        e.preventDefault()
        alert(i)
    })
    document.body.appendChild(a)
}
```

正确的写法
```javascript
var i
for (i = 0; i < 10; i++) {
    (function (i) {
        var a = document.createElement('a')
        a.innerHTML = i + '&lt;br>'
        a.addEventListener('click', function (e) {
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    })(i)
}
```
上面的回答已经结束了，但是还有一点可以优化，如果能做到，那将会给你加分。
提示一下，是关于 DOM 操作的性能问题的。这里先按下不表，等后面讲解性能问题的时候再说。有兴趣的可以先去查查`DocumentFragment`

### 如何理解作用域
根据我之前写过的[《深入理解JS原型和闭包》](http://www.cnblogs.com/wangfupeng1988/p/3977924.html)系列博客，详细讲解作用域和闭包。

### 实际开发中闭包的应用
闭包的实际应用，主要是用来封装变量。即把变量隐藏起来，不让外面拿到和修改。

```javascript
function isFirstLoad() {
    var _list = []

    return function (id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id)
            return true
        }
    }
}

// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
```
            </pre>
			<h2 class="h2">知识点</h2>
			<pre>
# 知识点
本节我们将全面梳理 JS 代码的执行过程，涉及到的概念有非常多。所以大家一定要耐心听，如果你此前不了解这一块，而通过本教程掌握了这方面知识，对你的基础知识将会有质的提醒。
## 执行上下文
先看下面的例子，你可能会对结果比较差异。当然，我不建议在实际开发中通过这种方式来炫技，我们这里演示纯粹是为了讲解知识点做一个铺垫。
```javascript
console.log(a)  // undefined
var a = 100

fn('zhangsan')  // 'zhangsan' 20
function fn(name) {
    age = 20
    console.log(name, age)
    var age
}
```

在一段 JS 脚本（即一个`&lt;script>`标签中）执行之前，会先创建一个**全局执行上下文**环境，
先把代码中即将执行的（内部函数的不算，因为你不知道函数何时执行）变量、函数声明（和“函数表达式”的区别）都拿出来。
变量先暂时赋值为`undefined`，函数则先声明好可使用。这一步做完了，然后再开始正式执行程序。再次强调，这是在代码执行之前才开始的工作。

另外，一个函数在执行之前，也会创建一个**函数执行上下文**环境，跟**全局上下文**差不多，
不过**函数执行上线文**中会多出`this` `arguments`和函数的参数。参数和`arguments`好理解，这里的`this`咱们需要专门讲解。

总结一下

- 范围：一段`&lt;script>`或者一个函数
- 全局：变量定义，函数声明
- 函数：变量定义，函数声明，this，arguments

## this
先搞明白一个很重要的概念 ———— **`this`的值是在执行的时候才能确认，定义的时候不能确认！**
为什么呢 ———— 因为`this`是执行上下文环境的一部分，而执行上下文需要在代码执行之前确定，而不是定义的时候。看如下例子

```javascript
            var a = {
                name: 'A',
                fn: function () {
                    console.log(this.name)
                }
            }
            a.fn()  // this === a
            a.fn.call({name: 'B'})  // this === {name: 'B'}
            var fn1 = a.fn
            fn1()  // this === window
                ```
`this`执行会有不同，主要集中在这几个场景中
    - 作为构造函数执行
    - 作为对象属性执行
    - 作为普通函数执行
    - 用于`call` `apply` `bind`
前两种情况咱们之前都介绍过了，这里只是统一的提出来，汇总一下，不再详细讲了。这里主要说第三种

```javascript
            function fn() {
                console.log(this)
            }
            fn()  // window
            fn.call({a:100})  // {a:100}  和 call 同理的还有 apply bind
                ```
## 作用域
作为有 JS 基础的同学，你应该了解 JS 没有块级作用域。例如
```javascript
            if (true) {
                var name = 'zhangsan'
            }
            console.log(name)
                ```
从上面的例子可以体会到作用域的概念，作用域就是一个独立的地盘，让变量不会外泄、暴露出去。
上面的`name`就被暴露出去了，因此，**JS 没有块级作用域，只有全局作用域和函数作用域**。
```javascript
            var a = 100
            function fn() {
                var a = 200
                console.log('fn', a)
            }
            console.log('global', a)
            fn()
                ```
全局作用域就是最外层的作用域，如果我们写了很多行 JS 代码，变量定义都没有用函数包括，那么他们就全部都在全局作用域中。这样的坏处就是很容易装车。
```javascript
            // 张三写的代码中
            var data = {a:100}

            // 李四写的代码中
            var data = {x:true}
                ```
这就是为何 jquery zepto 等库的源码，所有的代码都会放在`(function(){....})()`中。
因为放在里面的所有变量，都不会被外泄和暴露，不会污染到外面，不会对其他的库或者 JS 脚本造成影响。这是函数作用域的一个体现。

## 作用域链
首先认识一下什么叫做**自由变量**。如下代码中，`console.log(a)`要得到`a`变量，但是在当前的作用域中没有定义`a`（可对比一下`b`）。
当前作用域没有定义的变量，这成为**自由变量**。自由变量如何得到 ———— 向父级作用域寻找。

```javascript
            var a = 100
            function fn() {
                var b = 200
                console.log(a)
                console.log(b)
            }
            fn()
                ```

如果父级也没呢？再一层一层向上寻找，直到找到全局作用域还是没找到，就宣布放弃。这种一层一层的关系，就是**作用域链**。

```javascript
            var a = 100
            function F1() {
                var b = 200
                function F2() {
                    var c = 300
                    console.log(a)
                    console.log(b)
                    console.log(c)
                }
                F2()
            }
            F1()
                ```

## 闭包

直接看一个例子

```javascript
            function F1() {
                var a = 100
                return function () {
                    console.log(a)
                }
            }
            var f1 = F1()
            var a = 200
            f1()
                ```

自由变量将从作用域链中去寻找，但是**依据的是函数定义时的作用域链，而不是函数执行时**，以上这个例子就是闭包。闭包主要有两个应用场景：

- 函数作为返回值，上面的例子就是
- 函数作为参数传递，看以下例子

```javascript
            function F1() {
                var a = 100
                return function () {
                    console.log(a)
                }
            }
            function F2(f1) {
                var a = 200
                console.log(f1())
            }
            var f1 = F1()
            F2(f1)
                ```
## 扩展阅读
请参考我之前写过的文章[《深入理解JS原型和闭包系列博客》](http://www.cnblogs.com/wangfupeng1988/p/3977924.html)
            </pre>
		</div>
		<div class="one">
			<h1 id="js_thread">JS异步单线程与内置对象面试</h1>
			<h2 class="h2">其他知识点</h2>
			<pre>
# 知识点
## 正则表达式
如果对正则不熟悉，则先通过[《30分钟学会正则表达》](https://deerchao.net/tutorials/regex/regex.htm)来了解一下正则表达式的。

`test`函数的用法
```javascript
var ua = navigator.userAgent
var reg = /\bMicroMessenger\b/i
console.log(reg.test(ua))
```

用于`replace`的示例

```javascript
function trim(str) {
    return str.replace(/(^\s+)|(\s+$)/g, '')
}
```

`match`函数的用法

```javascript
var url = 'http://www.abc.com/path/xxx.html?a=10&b=20&c=30#topic'  // 后面的 #topic 也可能没有
var reg = /\?(.+?)(#|$)/
var matchResult = url.match(reg)
console.log(matchResult[1]) // a=10&b=20&c=30
```
略过正则表达式，不讲
-----------------
## 日期函数
日期函数最常用的 API 如下

```javascript
Date.now()  // 获取当前时间毫秒数
var dt = new Date()
dt.getTime()  // 获取毫秒数
dt.getFullYear()  // 年
dt.getMonth()  // 月（0 - 11）
dt.getDate()  // 日（0 - 31）
dt.getHours()  // 小时（0 - 23）
dt.getMinutes()  // 分钟（0 - 59）
dt.getSeconds()  // 秒（0 - 59）
```

## Math
Math 最常用的只有一个 API —— `Math.random()`
## 数组常用 API
- forEach
- every
- some
- sort
- map
- filter

forEach 举例
```javascript
var arr = [1,2,3]
arr.forEach(function (item, index) {
    // 遍历数组的所有元素
    console.log(index, item)
})
```

every 举例
```javascript
var arr = [1,2,3]
var result = arr.every(function (item, index) {
    // 用来判断所有的数组元素，都满足一个条件
    if (item < 4) {
        return ture
    }
})
console.log(result)
```
some 举例
```javascript
var arr = [1,2,3]
var result = arr.some(function (item, index) {
    // 用来判断所有的数组元素，只要有一个满足条件即可
    if (item < 2) {
        return ture
    }
})
console.log(result)
```

sort 举例
```javascript
var arr = [1,4,2,3,5]
var arr2 = arr.sort(function(a, b) {
    // 从小到大排序
    return a - b
    // 从大到小排序
    // return b - a
})
console.log(arr2)
```

map 举例
```javascript
var arr = [1,2,3,4]
var arr2 = arr.map(function(item, index) {
    // 将元素重新组装，并返回
    return '&lt;b>' + item + '&lt;/b>'
})
console.log(arr2)
```

filter 举例
```javascript
var arr = [1,2,3]
var arr2 = arr.filter(function (item, index) {
    // 通过某一个条件过滤数组
    if (item >= 2) {
        return true
    }
})
console.log(arr2)
```

## 对象常用 API

- for-in
```javascript
var obj = {
    x: 100,
    y: 200,
    z: 300
}
var key
for (key in obj) {
    // 注意这里的 hasOwnProperty，再讲原型链时候讲过了
    if (obj.hasOwnProperty(key)) {
        console.log(key, obj[key])
    }
}
```
            </pre>
			<h2 class="h2">解答</h2>
			<pre>
# 解答

## 获取`2017-06-10`格式的日期

```javascript
function formatDate(dt) {
    if (!dt) {
        dt = new Date()
    }
    var year = dt.getFullYear()
    var month = dt.getMonth() + 1
    var date = dt.getDate()
    if (month < 10) {
        // 强制类型转换
        month = '0' + month
    }
    if (date < 10) {
        // 强制类型转换
        date = '0' + date
    }
    // 强制类型转换
    return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)
```

## 获取随机数，要求是长度一直的字符串格式

使用`Math.random()`可获取字符串，但是返回的是一个小于 1 的小数，而且小数点后面长度不同

```javascript
var random = Math.random()
var random = random + '0000000000'  // 后面加上 10 个零
var random = random.slice(0, 10)
console.log(random)
```

## 写一个能遍历对象和数组的`forEach`函数

遍历数组使用`forEach`，而遍历对象使用`for in`，但是在实际开发中，可以使用一个函数就遍历两者，jquery 就有这样的函数

```javascript
function forEach(obj, fn) {
    var key
    if (obj instanceof Array) {
        // 准确判断是不是数组
        obj.forEach(function (item, index) {
            fn(index, item)
        })
    } else {
        // 不是数组就是对象
        for (key in obj) {
            fn(key, obj[key])
        }
    }
}

var arr = [1,2,3]
// 注意，这里参数的顺序换了，为了和对象的遍历格式一致
forEach(arr, function (index, item) {
    console.log(index, item)
})

var obj = {x: 100, y: 200}
forEach(obj, function (key, value) {
    console.log(key, value)
})
```



            </pre>
			<h2 class="h2">知识点</h2>
			<pre>
# 知识点

## 什么是异步

先看下面的 demo，根据程序阅读起来表达的意思，应该是先打印`100`，1秒钟之后打印`200`，最后打印`300`。但是实际运营根本不是那么回事。

```javascript
console.log(100)
setTimeout(function () {
    console.log(200)
}, 1000)
console.log(300)
```

再对比以下程序。先打印`100`，再弹出`200`（等待用户确认），最后打印`300`。这个运行效果就符合预期要求。

```javascript
console.log(100)
alert(200)  // 1秒钟之后点击确认
console.log(300)
```

这俩到底有何区别？———— 第一个示例中间的步骤根本没有阻塞接下来程序的运行，而第二个示例却阻塞了后面程序的运行。前面这种表现就叫做**异步**（后面这个叫做**同步**）

为何需要异步呢？如果第一个示例中间步骤是一个 ajax 请求，现在网络比较慢，请求需要5秒钟。如果是同步，这5秒钟页面就卡死在这里啥也干不了了。

何时需要异步:
                •在可能发生等待的情况
                •等待过程中不能像 alert 一样阻塞程序运行
                •因此，所以的“等待的情况”都需要异步


最后，前端 JS 脚本用到异步的场景主要有两个：

- 定时 `setTimeout` `setInverval`
- 网络请求，如 `ajax` `&lt;img>`加载
- 事件绑定（后面会有解释）

ajax 代码示例

```javascript
console.log('start')
$.get('./data1.json', function (data1) {
    console.log(data1)
})
console.log('end')
```

img 代码示例（常用语打点统计）

```javascript
console.log('start')
var img = document.createElement('img')
img.onload = function () {
    console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
```

事件绑定

```javascript
console.log('start')
document.getElementById('btn1').addEventListener('click', function () {
    alert('clicked')
})
console.log('end')
```

## 异步和单线程

JS 在客户端运行的时候，只有一个线程可运行，因此想要两件事儿同时干是不可能的。
如果没有异步，我们只能同步干，就像第二个示例一样，等待过程中卡住了，但是有了异步就没有问题了。那么单线程是如何实现异步的呢？

```javascript
console.log(100)
setTimeout(function () {
    console.log(200)
})
console.log(300)
```

那上面的示例来说，有以下几点。重点从这个过程中体会**单线程**这个概念，即事情都是一步一步做的，不能两件事儿一起做。

- 执行第一行，打印`100`
- 执行`setTimeout`后，传入`setTimeout`的函数会被暂存起来，不会立即执行。
- 执行最后一行，打印`300`
- 待所有程序执行完，处于空闲状态时，会立马看有没有暂存起来的要执行。
- 发现暂存起来的`setTimeout`中的函数无需等待时间，就立即来过来执行

下面再来一个`setTimeout`的例子。规则和上面的一样，只不过这里暂存起来的函数，需要等待 1s 之后才能被执行。

```javascript
console.log(100)
setTimeout(function () {
    console.log(200)
}, 1000)
console.log(300)
```

下面再来一个 ajax 的例子。规则也是一样的，只不过这里暂存起来的函数，要等待网络请求返回之后才能被执行，具体时间不一定。

```javascript
console.log(100)
$.get('./data.json', function (data) {
    console.log(200)
})
console.log(300)
```

最后再解释一下事件绑定，如下代码。其实事件绑定的实现原理和上面的是一样的，也是会把时间暂存，
但是要等待用户点击只有，才能被执行。原理是一样的，因此事件绑定在原理上来说，可以算作是异步。但是从设计上来说，还是分开好理解一些。

```javascript
console.log(100)
$btn.click(function () {
    console.log(200)
})
console.log(300)
```

**重点：异步的实现机制，以及对单线程的理解**

--------

下面的暂时先不讲

## 异步的问题和解决方案

异步遇到的最大的问题

- callback-hell
- 易读性差，即书写顺序和执行顺序不一致

```javascript
console.log('start')
$.get('./data1.json', function (data1) {
    console.log(data1)
    $.get('./data2.json', function (data2) {
        console.log(data2)
        $.get('./data3.json', function (data3) {
            console.log(data3)
            $.get('./data4.json', function (data4) {
                console.log(data4)
                // ...继续嵌套...
            })
        })
    })
})
console.log('end')
```

不过目前已经有了非常明确的解决方案 —— Promise，并且 Promise 放在 ES6 的标准中了。很遗憾本教程的范围不包括 ES6 ，因为 ES6 包含的内容太多了，放在这个教程中会很庞大，成本太高。

> 要想把异步讲全面，那得单独需要一门课程花5-7个小时去讲解（JS、jquery、ES6、node）。如果这样一讲，那就又带出了ES6的很多知识，又得花额外的时间去讲解，这样算下来，就得10多个小时。

我提供了一个参考链接，如果大家有本节课的基础，再去看参考链接的内容，应该能掌握异步更高级的知识。

## 参考和扩展阅读

- [深入理解 JavaScript 异步系列（1）——基础](http://www.cnblogs.com/wangfupeng1988/p/6513070.html)
- [深入理解 JavaScript 异步系列（2）—— jquery的解决方案](http://www.cnblogs.com/wangfupeng1988/p/6515779.html)
- [深入理解 JavaScript 异步系列（3）—— ES6 中的 Promise](http://www.cnblogs.com/wangfupeng1988/p/6515855.html)
- [深入理解 JavaScript 异步系列（4）—— Generator](http://www.cnblogs.com/wangfupeng1988/p/6532713.html)
- [深入理解 JavaScript 异步系列（5）—— async await](http://www.cnblogs.com/wangfupeng1988/p/6532734.html)

            </pre>
			<h2 class="h2">解答</h2>
			<pre>
# 解答


### 同步和异步的区别是什么？分别举一个同步和异步的例子

同步会阻塞代码执行，而异步不会。`alert`是同步，`setTimeout`是异步

### 一个关于`setTimeout`的笔试题

面试题中，`setTimeout`的基本是必会出现的

```javascript
// 以下代码执行后，打印出来的结果是什么
console.log(1)
setTimeout(function () {
    console.log(2)
}, 0)
console.log(3)
setTimeout(function () {
    console.log(4)
}, 1000)
console.log(5)
```

该题目的答案是`1 3 5 2 4`，不知道跟你答对了没有。具体的原理，我们后面再详细讲解。

### 前端使用异步的场景有哪些

- setTimeout setInterval
- 网络请求
- 事件绑定（可以说一下自己的理解）


            </pre>
		</div>
		<div class="one">
			<h1 class="h1" id="js-dom-bom">JS DOM、BOM事件常见面试题分析</h1>
			<h2 class="h2">基础知识</h2>
			<pre>
# 从基础知识到JSWebAPI
## JS基础知识
前面讲解的“JS基础知识”也叫做“ES基础知识”，其实是最基础的语法知识，它包含的内容

- 变量类型和计算
- 原型和原型链
- 闭包和作用域
- 异步和单线程
- 其他（如日期、Math、各种常用API）

这些知识如果从初学者看来，或者只从表面理解，对我们日常工作根本没什么作用。
                此前我们讲过，JS 基础语法内置的函数有`Object` `Array` `Boolean` `Number` `String` `Function` `Date` `RegExp` `Error`，另外还有`JSON` `Math`等常用的对象。
                我们拿到这些东西之后，貌似什么都干不了，连在网页中打印或者弹出一个提示都不能做。

但它是一个基础，越用JS你就越能发现。

## JS-Web-API

之前的知识什么都干不了，那怎么办呢？别着急，浏览器有它的处理方式。

此前说过，以上的JS基础知识（或者ES基础知识）是一句 ECMA-262 标准来制定的，浏览器会遵循这个标准，即在浏览器中使用此前讲过的变量、原型、闭包、异步都是没有问题的。

在此基础之上，浏览器还得让 JS 参与更多的事情，即让开发者能通过 JS 操作网页的各个地方，因此浏览器需要为 JS 在使用这些基础知识的基础上，再开发新的能力。而这些能力就需要遵循 W3C 的标准。

W3C 规定了很多内容，例如 html 规则、css 规则、JS 接口的规则，而和 JS 相关的并且面试过程中经常出现的有：

- DOM 操作
- BOM 操作
- 事件绑定
- ajax 请求（包括 http 协议）
- 存储

那 W3C 是如何规定的，以及浏览器又是如何执行的呢。举例来说，要在页面弹框就需要`alert('123')`，它学全了其实是`window.alert('123')`，那么浏览器需要做

- 定义一个 window 全局变量
- window 是一个对象，给它定义一个 alert 属性，属性值是一个函数（该函数可以调起浏览器的弹框）

当然，window 对象上有非常多的属性，这些都是浏览器给它赋值上的，当然开发者也可以对它进行扩展（符合对象的特性，可自由扩展属性）

再举一个例子，根据 id 获取一个元素是`document.getElementById('id')`，那么浏览器需要做

- 定义一个 document 全局变量
- 给它定义一个 getElementById 的属性，属性值是一个方法

但是，W3C 没有规定任何语法的东西，什么变量类型、原型、作用域、异步，它都不管。它只管定义一些可以在浏览器中用于 JS 操作页面的 API 和对象。

OK，最后，我们回过头来问一句，对于所有的 JS 来说（不是光考虑基础知识层面），它内置的全局变量有哪些？这个问题我们现在可能回答不全面，
                但是我们至少知道，除了之前讲过的那些，还有`window` `document`这两个，其实还有其他的很多很多个，但是现在都不用管。
                而且，以后只要是遇到我们没有定义，就直接拿来用的全局变量，那肯定就是浏览器内置的JS全局变量。
例如 `navigator.userAgent`

## 总结

常说的 JS（或浏览器中执行的JS）= JS基础知识（ECMA262标准）+ JS-Web-API（W3C标准）

从基础知识到JS-Web-API
	•JS-Web-API
	• 回顾 JS 基础知识
	•总结

回归JS基础知识
	•变量类型和计算
	•原型和原型链
	•闭包和作用域
	•异步和单线程
	•其他（如日期、Math、各种常用API）
	•内置函数：Object Array Boolean String ……
	•特点：表面看来并不能用于工作中开发代码
	•内置对象：Math JSON ……
	•我们连在网页弹出一句 hello world 都不能实现
JS-Web-API
	•JS基础知识：ECMA 262 标准
	•JS-Web-API：W3C 标准
	•W3C标准中关于 JS 的规定有：
		•DOM 操作
		•BOM 操作
		•事件绑定
		•ajax 请求（包括 http 协议）
		•存储

	•页面弹框是 window.alert(123)，浏览器需要做：
		•定义一个 window 全局变量，对象类型
		•给它定义一个 alert 属性，属性值是一个函数
	•获取元素 document.getElementById(id)，浏览器需要：
		•定义一个 document 全局变量，对象类型
		•给它定义一个 getElementById 的属性，属性值是一个函数
	•不管什么变量类型、原型、作用域和异步
	•但是W3C标准没有规定任何JS基础相关的东西
	•只管定义用于浏览器中JS操作页面的API和全局变量
	•之前讲过的 Object Array Boolean String Math JSON 等
	•全面考虑，JS内置的全局函数和对象有哪些？
	•刚刚提到的 window document
	•接下来还有继续讲到的所有未定义的全局变量，如 navigator.userAgent

总结
	•常说的JS（浏览器执行的JS）包含两部分：
	•JS基础知识（ECMA262标准）
	•JS-Web-API（W3C标准）

            </pre>
			<h2 class="h2">知识点</h2>
			<pre>

# 解答
### DOM 是哪种基本的数据结构？
树
### DOM 操作的常用 API 有哪些
- 获取节点，以及获取节点的 Attribute 和 property
- 获取父节点 获取子节点
- 新增节点，删除节点
### DOM 节点的 Attribute 和 property 有何区别
- property 只是一个 JS 属性的修改
- attr 是对 html 标签属性的修改


# 知识点
## 从 HTML 到 DOM
## DOM 的本质
讲 DOM 先从 html 讲起，讲 html 先从 XML 讲起。XML 是一种可扩展的标记语言，所谓可扩展就是它可以描述任何结构化的数据，它是一棵树！
```xml
&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;note>
  &lt;to>Tove&lt;/to>
  &lt;from>Jani&lt;/from>
  &lt;heading>Reminder&lt;/heading>
  &lt;body>Don't forget me this weekend!&lt;/body>
  &lt;other>
    &lt;a>&lt;/a>
    &lt;b>&lt;/b>
  &lt;/other>
&lt;/note>
```

HTML 是一个有既定标签标准的 XML 格式，标签的名字、层级关系和属性，都被标准化（否则浏览器无法解析）。同样，它也是一棵树。

```html
&lt;!DOCTYPE html>
&lt;html>
&lt;head>
    &lt;meta charset="UTF-8">
    &lt;title>Document&lt;/title>
&lt;/head>
&lt;body>
    &lt;div>
        &lt;p>this is p&lt;/p>
                &lt;/div>
&lt;/body>
&lt;/html>
```

我们开发完的 html 代码会保存到一个文档中（一般以`.html`或者`.htm`结尾），文档放在服务器上，浏览器请求服务器，
                这个文档被返回。因此，最终浏览器拿到的是一个文档而已，文档的内容就是 html 格式的代码。

但是浏览器要把这个文档中的 html 按照标准渲染成一个页面，此时浏览器就需要将这堆代码处理成自己能理解的东西，
                也得处理成 JS 能理解的东西，因为还得允许 JS 修改页面内容呢。

基于以上需求，浏览器就需要把 html 转变成 DOM，html 是一棵树，DOM 也是一棵树。
                对 DOM 的理解，可以暂时先抛开浏览器的内部因此，先从 JS 着手，即可以认为 DOM 就是 JS 能识别的 html 结构，一个普通的 JS 对象或者数组。

【附带一个 chrome Element 的截图】

## DOM 节点操作

### 获取 DOM 节点

```javascript
var div1 = document.getElementById('div1') // 元素
var divList = document.getElementsByTagName('div')  // 集合
console.log(divList.length)
console.log(divList[0])

var containerList = document.getElementsByClassName('.container') // 集合
var pList = document.querySelectorAll('p') // 集合
```

### prototype

DOM 节点就是一个 JS 对象，它符合之前讲述的对象的特征 ———— 可扩展属性

```javascript
var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width)  // 获取样式
p.style.width = '100px'  // 修改样式
console.log(p.className)  // 获取 class
p.className = 'p1'  // 修改 class

// 获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
```

### Attribute

property 的获取和修改，是直接改变 JS 对象，而 Attibute 是直接改变 html 的属性。两种有很大的区别

```javascript
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'imooc')
p.getAttribute('style')
p.setAttribute('style', 'font-size:30px;')
```

## DOM 树操作

新增节点

```javascript
var div1 = document.getElementById('div1')
// 添加新节点
var p1 = document.createElement('p')
p1.innerHTML = 'this is p1'
div1.appendChild(p1) // 添加新创建的元素
// 移动已有节点
var p2 = document.getElementById('p2')
div1.appendChild(p2)
```

获取父元素

```javascript
var div1 = document.getElementById('div1')
var parent = div1.parentElement
```

获取子元素

```javascript
var div1 = document.getElementById('div1')
var child = div1.childNodes
```

删除节点

```javascript
var div1 = document.getElementById('div1')
var child = div1.childNodes
div1.removeChild(child[0])
```

还有其他操作的API，例如获取前一个节点、获取后一个节点等，但是面试过程中经常考到的就是上面几个。

            </pre>
			<h2 class="h2">知识点</h2>
			<pre>
# 解答
## 如何检测浏览器的类型
```javascript
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
```

## 拆解url的各部分
```javascript
console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.pathname) // '/learn/199'
console.log(location.search)
console.log(location.hash)
```



# 知识点
DOM 是浏览器针对下载的 HTML 代码进行解析得到的 JS 可识别的数据对象。
                而 BOM（浏览器对象模型）是浏览器本身的一些信息的设置和获取，例如获取浏览器的宽度、高度，设置让浏览器跳转到哪个地址。

- navigator
- screen
- location
- history

这些对象就是一堆非常简单粗暴的 API 没人任何技术含量，讲起来一点意思都没有，大家去 MDN 或者 w3school 这种网站一查就都明白了。
                面试的时候，面试官基本不会出太多这方面的题目，因为只要基础知识过关了，这些 API 即便你记不住，上网一查也都知道了。


```javascript
// navigator
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)

// screen
console.log(screen.width)
console.log(screen.height)

// location
console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.pathname) // '/learn/199'
console.log(location.search)
console.log(location.hash)

// history
history.back()
history.forward()
```



            </pre>
			<h2 class="h2">知识点</h2>
			<pre>
# 解答
## 编写一个通用的事件监听函数
```js
function bindEvent(elem, type, selector, fn) {
    if (fn == null) {
        fn = selector
        selector = null
    }
    elem.addEventListener(type, function (e) {
        var target
        if (selector) {
            target = e.target
            if (target.matches(selector)) {
                fn.call(target, e)
            }
        } else {
            fn(e)
        }
    })
}
```
## 描述DOM事件冒泡流程
- DOM树形结构
- 事件会顺着触发元素网上冒泡
## 对于一个无线下拉加载图片的页面，如何给每个图片绑定事件
使用代理，优点
- 使代码简洁
- 减少浏览器的内存占用


# 知识点
## 事件绑定
```javascript
var btn = document.getElementById('btn1')
btn.addEventListener('click', function (event) {
    console.log('clicked')
})
```

### 通用的事件绑定函数
```js
function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', function(e) {
    e.preventDefault() // 阻止默认行为
    alert('clicked')
})
```
### 关于IE低版本的兼容性
前几年 IE 低版本还占有较高流量的时候，这个函数要写的比较复杂，因为 IE 低版本是使用`attachEvent`来绑定事件的。
                但是现在 IE 低版本基本也都没人用了，而且主流的网站也都慢慢不支持了，现在还有大部分流量在移动端，也不用考虑这个兼容。
因此，我不建议大家再去详细学习 IE 低版本的事件绑定了，你只需要上网简单一看，知道 IE 低版本不一样，
                有一些兼容性的问题，知道`attachEvent`这个最主要的函数，就够了。这就足够你去应对当前比较好的面试机会。
但是，如果有一个面试机会非得考察 IE 低版本的各种兼容性问题，你不会也没关系。
                因为你干脆就放弃这次面试，你面试成功了，进去估计也是天天维护那些老掉牙的支持 IE 低版本的项目，没什么进步空间的。
## 事件冒泡

```html
&lt;body>
    &lt;div id="div1">
        &lt;p id="p1">激活&lt;/p>
        &lt;p id="p2">取消&lt;/p>
        &lt;p id="p3">取消&lt;/p>
        &lt;p id="p4">取消&lt;/p>
    &lt;/div>
    &lt;div id="div2">
        &lt;p id="p5">取消&lt;/p>
        &lt;p id="p6">取消&lt;/p>
    &lt;/div>
&lt;/body>
```

对于以上 html 代码结构，点击`p1`时候进入激活状态，点击其他任何`p`都取消激活状态，如何实现？

```javascript
var p1 = document.getElementById('p1')
var body = document.body
bindEvent(p1, 'click', function (e) {
    e.stopPropatation() // 注释掉这一行，来体会事件冒泡
    alert('激活')
})
bindEvent(body, 'click', function (e) {
    alert('取消')
})
```

如果我们在`p1` `div1` `body`中都绑定了事件，它是会根据 DOM 的结构，来冒泡从下到上挨个执行的。但是我们使用`e.stopPropatation()`就可以阻止冒泡。

## 代理

我们设定一种场景，如下代码，一个`&lt;div>`中包含了若干个`&lt;a>`，而且还能继续增加。那如何快捷方便的为所有的`&lt;a>`绑定事件呢？

```html
&lt;div id="div1">
    &lt;a href="#">a1&lt;/a>
    &lt;a href="#">a2&lt;/a>
    &lt;a href="#">a3&lt;/a>
    &lt;a href="#">a4&lt;/a>
&lt;/div>
&lt;button>点击增加一个 a 标签&lt;/button>
```

这里就会用到事件代理，我们要监听`&lt;a>`的事件，但要把具体的事件绑定到`&lt;div>`上，然后看事件的触发点，是不是`&lt;a>`

```javascript
var div1 = document.getElementById('div1')
div1.addEventListener('click', function (e) {
    var target = e.target
    if (e.nodeName === 'A') {
        alert(target.innerHTML)
    }
})
```

那我们现在完善一下之前写过的通用事件绑定函数，加上事件代理

```javascript
function bindEvent(elem, type, selector, fn) {
    if (fn == null) {
        fn = selector
        selector = null
    }
    elem.addEventListener(type, function (e) {
        var target
        if (selector) {
            target = e.target
            if (target.matches(selector)) {
                fn.call(target, e)
            }
        } else {
            fn(e)
        }
    })
}
```

然后这样使用

```js
// 使用代理
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', 'a', function (e) {
    console.log(this.innerHTML)
})

// 不适用代理
var a = document.getElementById('a1')
bindEvent(div1, 'click', function (e) {
    console.log(a.innerHTML)
})
```

最后，使用代理的优点

- 使代码简洁
- 减少浏览器的内存占用


            </pre>
			<h2 class="h2">知识点</h2>
			<pre>
# 知识点

## XMLHttpRequest

```javascript
var xhr = new XMLHttpRequest()
xhr.open("GET", "/api", false)
xhr.onreadystatechange = function () {
    // 这里的函数异步执行，可参考之前 JS 基础中的异步模块
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            alert(xhr.responseText)
        }
    }
}
xhr.send(null)
```

有同学可能疑问：难道考我们这个，去上班就一定让我们写原生 JS 不让用 jquery 吗？———— 当然不是，
这就是考察基础知识掌握的一种手段，而掌握好了基础知识，并不一定马上就用上，但是一定会增加你干活的效率。毕竟招你进来是干活的，要快！

### 关于IE低版本的兼容性问题

最后，还得补充一下 IE 低版本的兼容性问题，它使用`var xhr = new ActiveXObject("Microsoft.XMLHTTP")`创建，这里我的建议跟讲事件那个章节一样，
                查查资料了解一下，不用详细看。如果工作之后不用，最好彻底忘掉，下次面试再扒出来本教程的文档看看就行了。

## 状态码说明

### readyState

xhr.readyState 的状态吗说明

- 0 - (未初始化）还没有调用send()方法
- 1 -（载入）已调用send()方法，正在发送请求
- 2 -（载入完成）send()方法执行完成，已经接收到全部响应内容
- 3 -（交互）正在解析响应内容
- 4 -（完成）响应内容解析完成，可以在客户端调用了

### status

http 状态吗有 `2xx` `3xx` `4xx` `5xx` 这几种，比较常用的有以下几种

 - 200 正常
 - 404 找不到资源
 - 5xx 服务器端出错了

## 跨域

### 什么是跨域

浏览器中有“同源策略”，即一个域下的页面中，无法通过 ajax 获取到其他域的接口。例如慕课网有一个接口`http://m.imooc.com/course/ajaxcourserecom?cid=459`，
                你自己的一个页面`http://www.yourname.com/page1.html`中的 ajax 无法获取这个接口。这正是命中了“同源策略”。
                如果浏览器哪些地方忽略了同源策略，那就是浏览器的安全漏洞，需要紧急修复。

url 哪些地方不同算作跨域？
- 协议
- 域名
- 端口

但是html中几个标签能逃避过同源策略——`&lt;script src="xxx">`、`&lt;img src="xxxx"/>`、`&lt;link href="xxxx">`，
                这俩标签的 src/href 可以加载其他域的资源，不受同源策略限制。

因此，这是三个标签可以做一些特殊的事情。

- `&lt;img>`可以做打点统计，因为统计方并不一定是同域的，在讲解JS基础知识异步的时候有过代码示例。
                除了能跨域之外，`&lt;img>`几乎没有浏览器兼容问题，它是一个非常古老的标签。
- `&lt;script>`和`&lt;link>`可以使用CDN，CDN基本都是其他域的链接。
- 另外`&lt;script>`还可以实现JSONP，能获取其他域接口的信息，接下来马上讲解。

但是请注意，所有的跨域请求方式，最终都需要信息提供方来做出相应的支持和改动，
                也就是要经过信息提供方的同意才行，否则接收方是无法得到他们的信息的，浏览器是不允许的。

### JSONP

首先，有一个概念要你要明白，例如访问`http://coding.m.imooc.com/classindex.html`的时候，
                服务器端就一定有一个`classindex.html`文件吗？—— 不一定，服务器可以拿到这个请求，然后动态生成一个文件，然后返回。
同理，`&lt;script src="http://coding.m.imooc.com/api.js">`也不一定加载一个服务器端的静态文件，服务器也可以动态生成文件并返回。OK，接下来正式开始。

例如我们的网站和慕课网，肯定不是一个域。我们需要慕课网提供一个接口，供我们来获取。首先，我们在自己的页面这样定义

```html
&lt;script>
window.callback = function (data) {
    // 这是我们跨域得到信息
    console.log(data)
}
&lt;/script>
```

然后慕课网给我提供了一个`http://coding.m.imooc.com/api.js`，内容如下（之前说过，服务器可动态生成内容）

```js
callback({x:100, y:200})
```

最后我们在页面中加入`&lt;script src="http://coding.m.imooc.com/api.js">&lt;/script>`，那么这个js加载之后，就会执行内容，我们就得到内容了

### 服务器端设置 http header

这是需要在服务器端设置的，作为前端工程师我们不用详细掌握，但是要知道有这么个解决方案。而且，现在推崇的跨域解决方案是这一种，比 JSONP 简单许多。

```js
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8011");  // 第二个参数填写允许跨域的域名称，不建议直接写 "*"
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");

// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
```

# 解答
## 手动编写一个 ajax，不依赖第三方库
```js
var xhr = new XMLHttpRequest()
xhr.open("GET", "/api", false)
xhr.onreadystatechange = function () {
    // 这里的函数异步执行，可参考之前 JS 基础中的异步模块
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            alert(xhr.responseText)
        }
    }
}
xhr.send(null)
```
## 跨域的几种实现方式
- JSONP
- 服务器端设置 http header


            </pre>
		</div>
    </div>
</div>
</body>
</html>